#pragma once
#include <iostream>
#include <mutex>
#include <fstream>
#include <sstream>
#include <string>
#include <cstdio>
#include <ctime>
#include <filesystem>
#include <memory>
#include <unistd.h>
namespace MY
{
    const std::string gsep = "\r\n";
    class logStrategy
    {
    private:
    public:
        logStrategy() = default;
        ~logStrategy() = default;
        virtual bool syncLog(const std::string &logInfo) = 0;
    };
    // 显示器上打印
    class logScreenStrategy : public logStrategy
    {
    private:
        std::mutex _mtx;

    public:
        bool syncLog(const std::string &logInfo) override
        {
            std::lock_guard<std::mutex> lock(_mtx);
            std::cout << logInfo << gsep;
            return true;
        }
    };
    // 指定文件中打印
    const std::string defaultFileName = "log";
    const std::string defaultPathName = "./log";
    class logFileStrategy : public logStrategy
    {
    private:
        std::mutex _mtx;
        std::string _filename;
        std::string _pathname;

    public:
        logFileStrategy(const std::string &filename = defaultFileName, const std::string &pathname = defaultPathName)
            : _filename(filename), _pathname(pathname)
        {
            std::lock_guard<std::mutex> lock(_mtx);
            if (std::filesystem::exists(_pathname))
                return;
            try
            {
                std::filesystem::create_directories(pathname);
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << '\n';
            }
        }
        bool syncLog(const std::string &logInfo) override
        {
            std::lock_guard<std::mutex> lock(_mtx);
            std::string ab_path = _pathname + (_pathname.back() == '/' ? "" : "/") + _filename;
            std::ofstream out(ab_path, std::ios::app);
            if (!out.is_open())
                return false;
            out << logInfo << gsep;
            out.close();
            return true;
        }
    };
    // log等级
    enum class logLevel
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };
    std::string levelStr(logLevel level)
    {
        switch (level)
        {
        case logLevel::DEBUG:
            return "DEBUG";

        case logLevel::INFO:
            return "INFO";

        case logLevel::WARNING:
            return "WARNING";

        case logLevel::ERROR:
            return "ERROR";

        case logLevel::FATAL:
            return "FATAL";

        default:
            return "UNKNOW";
        }
    }
    std::string getTimeStamp()
    {
        time_t t = time(nullptr);
        struct tm c_tm;
        localtime_r(&t, &c_tm);
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d %02d-%02d-%02d",
                 c_tm.tm_year + 1900,
                 c_tm.tm_mon + 1,
                 c_tm.tm_mday,
                 c_tm.tm_hour,
                 c_tm.tm_min,
                 c_tm.tm_sec);
        return buffer;
    }
    // 打印日志
    class log
    {
    private:
        std::unique_ptr<logStrategy> _strategy;
        class message
        {
        private:
            std::string _time;
            logLevel _level;
            pid_t _pid;
            std::string _file;
            int _line;
            std::string _info;
            log& _log;

        public:
            message(const logLevel&level,const std::string& file,int line,log& log)
            :_time(getTimeStamp())
            ,_level(level)
            ,_pid(getpid())
            ,_file(file)
            ,_line(line)
            ,_log(log)
            {
                std::stringstream ss;
                ss<<"["<<_time<<"] "
                  <<"["<<levelStr(_level)<<"] "
                  <<"["<<_pid<<"] "
                  <<"["<<_file<<"] "
                  <<"["<<_line<<"] "
                  <<" - ";
                  _info=ss.str();
            }
            ~message()
            {
                if(_log._strategy){
                    _log._strategy->syncLog(_info);
                }
            }
            template<typename T>
            message& operator<<(const T& in)
            {
                std::stringstream ss;
                ss<<in;
                _info+=ss.str();
                return *this;
            }

        };
    public:
        log(){enableLogScreenStrategy();}
        void enableLogScreenStrategy(){_strategy=std::make_unique<logScreenStrategy>();}
        void enableLogFileStrategy(const std::string &filename = defaultFileName, const std::string &pathname = defaultPathName){_strategy=std::make_unique<logFileStrategy>(filename,pathname);}
        message operator()(const logLevel&level,const std::string& file,const int line){return message(level,file,line,*this);};
    };
    //define global log
    log logger;
    #define LOG(level) logger(level,__FILE__,__LINE__)
    #define LOG_SCREEN_STRATEGY_ENABLE logger.enableLogScreenStrategy()
    #define LOG_FILE_STRATEGY_ENABLE logger.enableLogFileStrategy()

}